package com.zodiac.alioth.web.security.auth;


import com.alibaba.fastjson.JSON;
import com.zodiac.alioth.common.constants.CoreConst;
import com.zodiac.alioth.common.constants.RedisKeyPrefixConst;
import com.zodiac.alioth.common.enums.ApiRespCodeEnum;
import com.zodiac.alioth.common.holder.SpringContextHolder;
import com.zodiac.alioth.common.model.vo.ApiResponse;
import com.zodiac.alioth.service.redis.RedisService;
import com.zodiac.alioth.service.redis.impl.RedisServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.Objects;

/**
 * 用户认证
 *
 * @author insight
 */
@Slf4j
public class DefaultAuth implements AppAuthChecker.Callback<DefaultAuth.Status> {

    public static final String SESSION_KEY = "J_USER_KEY";
    public static final String SESSION_KEY_UT = "u_t";

    private AuthUser getAuthUser(String xToken) {
        RedisService redisService = (RedisServiceImpl) SpringContextHolder.getBean("redisServiceImpl");
        return redisService.get(RedisKeyPrefixConst.X_TOKEN_PREFIX + xToken);
    }

    @Override
    public String getUserName(Status status) {
        return status.user.getUserName();
    }

    @Override
    public String[] getRoleNames(Status status) {
        return status.user.getRoles();
    }

    @Override
    public Status onStart(AppAuthChecker.RequestContext requestContext) {
        HttpServletRequest httpServletRequest = requestContext.httpServletRequest;

        AuthUser user = null;

        try {
            String token = httpServletRequest.getHeader(CoreConst.HEADER_X_TOKE);
            String expireTime = httpServletRequest.getHeader(CoreConst.HEADER_X_TOKEN_EXPIRE);
            if (StringUtils.isNotEmpty(token) && StringUtils.isNotEmpty(expireTime)) {
                AuthUser authUser = this.getAuthUser(token);
                if (Objects.nonNull(authUser)) {
                    long tokenExpireTime = authUser.getTokenExpireTime();
                    if (Long.parseLong(expireTime) == tokenExpireTime && System.currentTimeMillis() < tokenExpireTime) {
                        user = authUser;
                    }
                } else {
                    log.info("token is expired, token={}", token);
                }
            }


        } catch (Exception e) {
            log.warn("Failed to read user from redis: " + httpServletRequest.getHeader(CoreConst.HEADER_X_TOKE), e);
        }

        if (user == null) {
            // 创建匿名用户
            user = new AuthUser();

            AuthUser.cleanThreadLocal();
        }

        AuthUser.setCurrentUser(user);

        return new Status(requestContext, user);
    }


    @Override
    public void onAllow(Status status) throws ServletException {

    }

    @Override
    public void onDeny(Status status) throws ServletException {
        response401(status.requestContext.httpServletResponse);
    }

    private void response401(HttpServletResponse response) {
        response.addHeader("Cache-Control", "no-cache");
        response.setContentType("application/json;charset=UTF-8");
        try (PrintWriter out = response.getWriter()) {
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            out.write(JSON.toJSONString(ApiResponse.getResult(ApiRespCodeEnum.UNAUTHORIZED)));
        } catch (Exception e) {
            log.error("response401 error", e);
        }
    }

    static class Status {
        private final AppAuthChecker.RequestContext requestContext;
        private AuthUser user;

        public Status(AppAuthChecker.RequestContext requestContext, AuthUser user) {
            this.requestContext = requestContext;
            this.user = user;
        }
    }

}
